package com.itaming.lycheeframework.jackson.config;

import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.itaming.lycheeframework.jackson.modules.BigNumberModule;
import com.itaming.lycheeframework.jackson.modules.Jsr310DateTimeModule;
import com.itaming.lycheeframework.jackson.properties.LycheeJacksonProperties;
import com.itaming.lycheeframework.jackson.provider.LycheeSerializerProvider;
import com.itaming.lycheeframework.support.datetime.FlexibleDateFormat;
import com.itaming.lycheeframework.support.utils.LocaleUtil;
import com.itaming.lycheeframework.support.utils.TimeZoneUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

/**
 * Jackson模块配置类
 *
 * @author A.Ming
 */
@AutoConfiguration(before = {JacksonAutoConfiguration.class})
@ConditionalOnClass(ObjectMapper.class)
@EnableConfigurationProperties({LycheeJacksonProperties.class})
@RequiredArgsConstructor
public class LycheeJacksonConfiguration {

    /**
     * Jackson配置参数
     */
    private final LycheeJacksonProperties properties;

    /**
     * ObjectMapper
     *
     * @param builder Jackson2ObjectMapperBuilder
     * @return ObjectMapper
     */
    @Bean
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder
            // 地点
            .locale(LocaleUtil.getDefault())
            // 时区
            .timeZone(TimeZoneUtil.getDefault())
            // 日期时间
            .dateFormat(new FlexibleDateFormat())
            // Java8日期时间
            .modules(modules -> modules.add(new Jsr310DateTimeModule()))
            // 忽略失败：空对象(没有可序列化的字段)
            .failOnEmptyBeans(false)
            // 忽略失败：反序列化时属性不存在(json中存在但java类中不存在)
            .failOnUnknownProperties(false)
            // 禁用默认的时间戳格式
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            // 解析功能
            .featuresToEnable(
                // 允许包含非引号控制字符(值小于32的ASCII字符：包含制表符和换行符)
                JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(),
                // 允许反斜杠转义任何字符
                JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(),
                // 允许使用单引号包裹字段名
                JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature()
            )
            .createXmlMapper(false)
            .postConfigurer(this::dynamicConfigure)
            .build();
    }

    /**
     * 动态配置{@link ObjectMapper}对象
     *
     * @param objectMapper {@link ObjectMapper}对象
     */
    private void dynamicConfigure(ObjectMapper objectMapper) {
        // 处理大数值
        if (properties.isBigNumberToString()) {
            objectMapper.registerModule(new BigNumberModule());
        }

        // 处理属性命名策略
        if (properties.isSnakeCaseProperty()) {
            objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        }

        // 处理null转为空值
        if (properties.isNullToEmpty()) {
            objectMapper.setSerializerProvider(new LycheeSerializerProvider());
        }
    }

}
